//+------------------------------------------------------------------------
//
//  Microsoft Forms
//  Copyright (C) Microsoft Corporation, 1996
//
//  File:       padauto.cxx
//
//  Contents:   CPadDoc class.
//
//-------------------------------------------------------------------------

#include "padhead.hxx"

#ifndef X_PADDEBUG_HXX_
#define X_PADDEBUG_HXX_
#include "paddebug.hxx"
#endif

enum SENDKEY_FLAGS
{
    FLAG_SHIFT      = 1,
    FLAG_CONTROL    = 2,
    FLAG_MENU       = 4
};

//---------------------------------------------------------------------------
//?
//  Member:     CPadDoc::EnqueueKeyAction
//
//  Synopsis:   Enqueue action for later processing.  Actions are
//              processed by DoKeyAction in response to WM_USER messages.
//
//---------------------------------------------------------------------------

void
CPadDoc::EnqueueKeyAction(DWORD dwFlags, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if ( IsWindow( g_hwndActiveWindow ) )
    {
        PostMessage(g_hwndActiveWindow, msg, wParam, lParam);
    }
    else
    {
        PADTHREADSTATE * pts = GetThreadState();
        SENDKEY_ACTION *paction;

        if (pts->caction >= ARRAY_SIZE(pts->aaction) - 1)
        {
            Assert(0 && "SendKey buffer overflow.");
            return;
        }

        paction = &pts->aaction[pts->caction++];
        paction->msg  = msg;
        paction->wParam = wParam;
        paction->lParam = lParam;
        paction->dwFlags = dwFlags;

        if (pts->caction == 1)
        {
            PostMessage(_hwnd, WM_DOKEYACION, 0, 0);
        }
    }
}

//---------------------------------------------------------------------------
//
//  Member:     CPadDoc::DoKeyAction
//
//  Synopsis:   This method is called in response to a WM_USER message
//              and processes on keyboard action at a time.  If more
//              keyboard actions are waiting, it posts a new WM_USER
//              message. 
//
//---------------------------------------------------------------------------

void
CPadDoc::DoKeyAction()
{
    PADTHREADSTATE * pts = GetThreadState();
    SENDKEY_ACTION * paction;
    BYTE             abState[256];
    HWND             hwnd;
    MSG              msg = {0};
    
    Assert(pts->iaction < pts->caction);
    paction = &pts->aaction[pts->iaction++];   

    if (!_fKeyStateLocked)
    {
        if (GetKeyboardState(abState))
        {
            abState[VK_SHIFT] = (paction->dwFlags & FLAG_SHIFT) ? 0x80 : 0;
            abState[VK_CONTROL] = (paction->dwFlags & FLAG_CONTROL) ? 0x80 : 0;
            abState[VK_MENU] = (paction->dwFlags & FLAG_MENU) ? 0x80 : 0;
            SetKeyboardState(abState);
        }
    }

    msg.message = paction->msg;
    msg.wParam = paction->wParam;
    msg.lParam = paction->lParam;

    if (GetLastActivePopup(_hwnd) == _hwnd)
    {
        if ((hwnd = GetFocus()) != NULL)
        {
            // use focus window if we can get one
        }
        else if (_pInPlaceActiveObject &&
            OK(_pInPlaceActiveObject->GetWindow(&hwnd)))
        {
            // use inplace active object hwnd
        }
        else if (_pInPlaceObject &&
            OK(_pInPlaceObject->GetWindow(&hwnd)))
        {
            // use inplace object window
        }
        else
        {
            // use our window
            hwnd = _hwnd;
        }

        msg.hwnd = hwnd;
        if (!OnTranslateAccelerator(&msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            // Retrieve the WM_CHAR/WM_SYSCHAR message that may have been
            // generated by TranslateMessage() and dispatch it
            // immediately, so that it gets processed with the right
            // key state set.
            if (PeekMessage(&msg,
                     msg.hwnd, 
                    (paction->dwFlags & FLAG_MENU) ? WM_SYSCHAR : WM_CHAR, 
                    (paction->dwFlags & FLAG_MENU) ? WM_SYSCHAR : WM_CHAR,
                    PM_REMOVE))

            {
                if (!OnTranslateAccelerator(&msg))
                    DispatchMessage(&msg);
            }
        }
    }
    else
    {
        // Active window is not the pad window.  Make it be the foreground
        // window so that the focus is correct.
        
        if (GetForegroundWindow() != GetLastActivePopup(_hwnd))
        {
            SetForegroundWindow(GetLastActivePopup(_hwnd));
        }

        // Send the message to the foreground window.
        msg.hwnd = GetFocus();

        if (paction->dwFlags & (FLAG_MENU|FLAG_SHIFT|FLAG_CONTROL))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            // Retrieve the WM_CHAR/WM_SYSCHAR message that may have been
            // generated by TranslateMessage() and dispatch it
            // immediately, so that it gets processed with the right
            // key state set.
            if (PeekMessage(&msg,
                        msg.hwnd, 
                        (paction->dwFlags & FLAG_MENU) ? WM_SYSCHAR : WM_CHAR, 
                        (paction->dwFlags & FLAG_MENU) ? WM_SYSCHAR : WM_CHAR,
                        PM_REMOVE))

             {
                DispatchMessage(&msg);
             }
        }
        else
            PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
    }

    if (pts->iaction < pts->caction)
    {
        PostMessage(_hwnd, WM_DOKEYACION, 0, 0);
    }
    else
    {
        if (!_fKeyStateLocked)
        {
            abState[VK_MENU] = 0;
            abState[VK_SHIFT] = 0;
            abState[VK_CONTROL] = 0;
            SetKeyboardState(abState);
        }
        pts->iaction = 0;
        pts->caction = 0;

        if (_pDebugWindow && pts->fDebugWindowInFront)
        {
            SetForegroundWindow(_pDebugWindow->_hwnd);
        }
    }
}

//---------------------------------------------------------------------------
//
//  Member: CPadDoc::LockKeyState, IPad
//
//---------------------------------------------------------------------------

HRESULT
CPadDoc::LockKeyState(VARIANT_BOOL fShift, VARIANT_BOOL fControl, VARIANT_BOOL fAlt)
{
    if (!_fKeyStateLocked)
    {
        BYTE    abState[256];
    
        _fKeyStateLocked = TRUE;
        if (GetKeyboardState(abState))
        {
            abState[VK_SHIFT] = (fShift) ? 0x80 : 0;
            abState[VK_CONTROL] = (fControl) ? 0x80 : 0;
            abState[VK_MENU] = (fAlt) ? 0x80 : 0;
            SetKeyboardState(abState);
        }
    }
    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CPadDoc::UnlockKeyState, IPad
//
//---------------------------------------------------------------------------

HRESULT
CPadDoc::UnlockKeyState()
{
    if (_fKeyStateLocked)
    {
        BYTE    abState[256];
    
        _fKeyStateLocked = FALSE;
        if (GetKeyboardState(abState))
        {
            abState[VK_MENU] = 0;
            abState[VK_SHIFT] = 0;
            abState[VK_CONTROL] = 0;
            SetKeyboardState(abState);
        }
    }
    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CPadDoc::SendKeys, IPad
//
//---------------------------------------------------------------------------

HRESULT
CPadDoc::SendKeys(BSTR bstrKeys, VARIANT_BOOL fWait)
{
    PADTHREADSTATE * pts = GetThreadState();
    TCHAR * pch;

    if (!g_hwndActiveWindow && !_pInPlaceActiveObject)
        return S_OK;

    if ( g_hwndActiveWindow )
    {
        if ( IsWindow( g_hwndActiveWindow ) )
        {
            SetForegroundWindow( g_hwndActiveWindow );
        }
        else
        {
            return E_FAIL;
        }
    }
    else if (_pDebugWindow &&
        GetForegroundWindow() == _pDebugWindow->_hwnd)
    {
        pts->fDebugWindowInFront = TRUE;
        SetForegroundWindow(GetLastActivePopup(_hwnd));
    }
    else
    {
        pts->fDebugWindowInFront = FALSE;
    }

    pch = bstrKeys;

    while (*pch != 0)
    {
        pch = SendKey(pch, 0);
    }

    if (fWait)
    {
        Run(TRUE);
    }

    return S_OK;
}

//---------------------------------------------------------------------------
//
//  Member: CPadDoc::SendKey, IPad
//
//---------------------------------------------------------------------------
          
TCHAR *
CPadDoc::SendKey(TCHAR * pch, DWORD dwFlags)
{
    switch (*pch)
    {
    case _T('+'):
        pch = SendKey(++pch, dwFlags | FLAG_SHIFT);
        break;
    
    case _T('^'):
        pch = SendKey(++pch, dwFlags | FLAG_CONTROL);
        break;
     
    case _T('%'):
        pch = SendKey(++pch, dwFlags | FLAG_MENU);
        break;

    case _T('{'):
        pch = SendSpecial(++pch, dwFlags);
        break;

    case _T('~'):
        EnqueueKeyAction(dwFlags, WM_KEYDOWN, VK_RETURN, (LPARAM)1);

        // Set the bits 29-31 as per WM_KEYUP documentation. Otherwise, TranslateMessage()
        // generates spurious WM_CHAR.
        EnqueueKeyAction(dwFlags, WM_KEYUP, VK_RETURN, (LPARAM)0xc0000001);
        pch++;
        break;

    case _T('('):
        pch++;
        while (*pch != _T(')') && *pch != 0)
            pch = SendKey(pch, dwFlags);
        if(*pch == _T(')'))
            pch++;
        break;

    default:
        {
            WORD vKeyCode = VkKeyScan(*pch++);
        
            if (dwFlags & FLAG_MENU)
                EnqueueKeyAction(dwFlags | HIBYTE(vKeyCode), WM_SYSKEYDOWN, LOBYTE(vKeyCode), 0x20000001);
            else
                EnqueueKeyAction(dwFlags | HIBYTE(vKeyCode), WM_KEYDOWN, LOBYTE(vKeyCode),1);

            // Set the bits 29-31 as per WM_KEYUP documentation. Otherwise, TranslateMessage()
            // generates spurious WM_CHAR.            
            if (dwFlags & FLAG_MENU)
                EnqueueKeyAction(dwFlags | HIBYTE(vKeyCode), WM_SYSKEYUP, LOBYTE(vKeyCode), 0xc0000001);
            else
                EnqueueKeyAction(dwFlags | HIBYTE(vKeyCode), WM_KEYUP, LOBYTE(vKeyCode), 0xc0000001);
        }
        break;
    }

    return pch;
}

struct KEYENTRY
{
    TCHAR achName[20];
    int nKey;
};

KEYENTRY aKeyEntry[] =
{
    {_T("BACKSPACE"),   VK_BACK},
    {_T("BS"),          VK_BACK},
    {_T("BKSP"),        VK_BACK},
    {_T("BREAK"),       VK_PAUSE},
    {_T("CAPSLOCK"),    VK_CAPITAL},
    {_T("DELETE"),      VK_DELETE},
    {_T("DEL"),         VK_DELETE},
    {_T("DOWN"),        VK_DOWN},
    {_T("END"),         VK_END},
    {_T("ENTER"),       VK_RETURN},
    {_T("ESC"),         VK_ESCAPE},
    {_T("HOME"),        VK_HOME},
    {_T("INSERT"),      VK_INSERT},
    {_T("LEFT"),        VK_LEFT},
    {_T("NUMLOCK"),     VK_NUMLOCK},
    {_T("PGDN"),        VK_NEXT},
    {_T("PGUP"),        VK_PRIOR},
    {_T("PRTSC"),       VK_SNAPSHOT},
    {_T("RIGHT"),       VK_RIGHT},
    {_T("SCROLLLOCK"),  VK_SCROLL},
    {_T("TAB"),         VK_TAB},
    {_T("UP"),          VK_UP},
    {_T("HELP"),        VK_HELP},
    {_T("NUMPAD0"),     VK_NUMPAD0},
    {_T("NUMPAD0"),     VK_NUMPAD1},
    {_T("NUMPAD0"),     VK_NUMPAD2},
    {_T("NUMPAD0"),     VK_NUMPAD3},
    {_T("NUMPAD0"),     VK_NUMPAD4},
    {_T("NUMPAD0"),     VK_NUMPAD5},
    {_T("NUMPAD0"),     VK_NUMPAD6},
    {_T("NUMPAD0"),     VK_NUMPAD7},
    {_T("NUMPAD0"),     VK_NUMPAD8},
    {_T("NUMPAD0"),     VK_NUMPAD9},
    {_T("MULTIPLY"),    VK_MULTIPLY},
    {_T("ADD"),         VK_ADD},
    {_T("SEPARATOR"),   VK_SEPARATOR},
    {_T("SUBTRACT"),    VK_SUBTRACT},
    {_T("DECIMAL"),     VK_DECIMAL},
    {_T("DIVIDE"),      VK_DIVIDE},
    {_T("F1"),          VK_F1},
    {_T("F2"),          VK_F2},
    {_T("F3"),          VK_F3},
    {_T("F4"),          VK_F4},
    {_T("F5"),          VK_F5},
    {_T("F6"),          VK_F6},
    {_T("F7"),          VK_F7},
    {_T("F8"),          VK_F8},
    {_T("F9"),          VK_F9},
    {_T("F10"),         VK_F10},
    {_T("F11"),         VK_F11},
    {_T("F12"),         VK_F12},
    {_T("F13"),         VK_F13},
    {_T("F14"),         VK_F14},
    {_T("F15"),         VK_F15},
    {_T("F16"),         VK_F16},
    {_T("KANJI"),       VK_KANJI},
    {_T(""),            0}
};

TCHAR *
CPadDoc::SendSpecial(TCHAR * pch, DWORD dwFlags)
{
    KEYENTRY *pKeyEntry = aKeyEntry;
    TCHAR * pch1, * pch2;
    TCHAR ch1, ch2;
    int cRepeat = 1;

    Assert (pch[-1] == _T('{'));

    pch1 = pch;
    while(*pch1 != _T(' ') && *pch1 != _T('}') && *pch1)
        pch1++;

    ch1 = *pch1;
    *pch1 = 0;

    pch2 = pch1 + 1;

    if(ch1 == _T(' '))
    {
        while(*pch2 != _T('}') && *pch2)
            pch2++;

        ch2 = *pch2;
        *pch2 = 0;

        cRepeat = _wtoi(pch1 + 1);

        *pch2 = ch2;

        pch2++;
    }

    while(pKeyEntry->nKey != 0)
    {
        if(StrCmpIC(pch, pKeyEntry->achName) == 0)
        {
            for(int i = 0; i < cRepeat; i++)
            {
                if (dwFlags & FLAG_MENU)
                    EnqueueKeyAction(
                            dwFlags,
                            WM_SYSKEYDOWN,
                            pKeyEntry->nKey,
                            (LPARAM) 0x20000001);
                else
                    EnqueueKeyAction(
                            dwFlags,
                            WM_KEYDOWN,
                            pKeyEntry->nKey,
                            (LPARAM)1);
            }
            if (dwFlags & FLAG_MENU)
                EnqueueKeyAction(
                        dwFlags,
                        WM_SYSKEYUP,
                        pKeyEntry->nKey,
                        (LPARAM) 0xC0000001);
            else
                // Set the bits 29-31 as per WM_KEYUP documentation. Otherwise, TranslateMessage()
                // generates spurious WM_CHAR.            
                EnqueueKeyAction(
                        dwFlags,
                        WM_KEYUP,
                        pKeyEntry->nKey,
                        (LPARAM)0xC0000001);
            break;
        }

        pKeyEntry++;
    }

    // ??????? Ask Gary what this could be
    if(pKeyEntry->nKey == 0 && *pch != 0 && pch1 == pch + 1)
    {
        for(int i = 0; i < cRepeat; i++)
            EnqueueKeyAction(dwFlags, WM_CHAR, *pch, 1);
    }

    *pch1 = ch1;

    return pch2;
}


// Send keys in such a way that the IME can see them.
TCHAR *
   CPadDoc::SendIMESpecial(TCHAR * pch, DWORD dwFlags)
{
    KEYENTRY *pKeyEntry = aKeyEntry;
    TCHAR * pch1, * pch2;
    TCHAR ch1, ch2;

    Assert (pch[-1] == _T('{'));

    // Eat leading spaces.
    pch1 = pch;
    while(*pch1 != _T(' ') && *pch1 != _T('}') && *pch1)
        pch1++;

    ch1 = *pch1;
    *pch1 = 0;

    pch2 = pch1 + 1;

    // Eat trailling spaces.
    if(ch1 == _T(' '))
    {
        while(*pch2 != _T('}') && *pch2)
            pch2++;

        ch2 = *pch2;
        *pch2 = 0;

        *pch2 = ch2;

        pch2++;
    }

    while(pKeyEntry->nKey != 0)
    {
        if(StrCmpIC(pch, pKeyEntry->achName) == 0)
        {
            keybd_event((BYTE)pKeyEntry->nKey, 0, 0, 0);
            keybd_event((BYTE)pKeyEntry->nKey, 0, KEYEVENTF_KEYUP, 0);
            break;
        }

        pKeyEntry++;
    }

    *pch1 = ch1;

    return pch2;
}
